(function() {
    var Yz = window.Yz = function() {};

    /**
     *  make the function console.log available everywhere
     *
     */
    if (typeof console != "undefined") {
        console.log("Yz init");
    } else {
        window.console = { log: function() {} };
    }

    Yz.webPrefix = "/";
    Yz.webRoot = "/";
    Yz.wwwDir = Yz.webRoot + "www/";
    Yz.appName = "Yz";


    /**
     *  block user interface
     *  TODO really block, ie. overlay div
     */
    Yz.block = function()
    {
        console.log("block");
        if (document.body)
            document.body.style.cursor = "wait";
    }

    /**
     *  unblock user interface
     */
    Yz.unblock = function()
    {
        console.log("unblock");
        if (document.body)
            document.body.style.cursor = "";
    }

    /**
     *  load a class in form AppName.ClassName[.subclassName]*
     *  TODO make more generic to load any class
     */
    Yz.loadClass = function(className, callback)
    {
        if (typeof(className) != 'string')
            throw "Yz.loadClass(): Invalid parameter for className";

        // test if a class is loaded
        var classPath = className.split('.'),   // "AppName.ClassName" -> [AppName, ClassName]
            appName = 1==classPath.length       // AppName missing ?
                ? Yz.appName                    // yes/absent -> use default appName
                : classPath[0],                 // no/present -> use AppName
            isLoaded = true,                    // assertion
            orig = window;
        for (var c=0; c<classPath.length; c++) {
            console.log("Class path component: " + classPath.slice(0,c+1).join('.'));
            if (! (orig instanceof Object))
                throw "Yz.loadClass(): Invalid object type: " + classPath.slice(0,c+1).join('.');
            if (classPath[c] in orig)
                orig = orig[classPath[c]];
            else
                isLoaded = false;
        }
        if (isLoaded)
            typeof callback == "function" && callback();
        else {
            try {
                Yz.fetchScript(
                    Yz.wwwDir + appName + '/js/' + className + ".js",
                    function() {
                        console.log("Loaded class: "+className);
                        callback && callback();
                    }
                );
            } catch (ex) {
                Yz.Alert("Failed to load plugin " + plugin + ": " + ex);
                throw "Failed to load plugin " + plugin + ": " + ex;
                //return false;
            }
        }
        return Yz;
    }

    /**
     *  `require' jQuery
     *  on successful load (or if already loaded), execute callback
     *  @param callback: void function(void)
     */
    Yz.jQuery = Yz.jquery = function(callback)
    {
        if (window.jQuery) {
            // if jQuery already loaded, execute callback immediately
            console.log("jQuery version: " + window.jQuery.fn.jquery );
            typeof callback == "function" && callback();
        } else {
            var self = Yz.jquery;
            // create a stack of callbacks to execute on successful load of jQuery
            if (self._callbacks) {
                console.log("Yz.jQuery already called, but jquery.js not loaded yet");
            } else {
                self._callbacks = [];
                Yz.fetchScript(

                    // path
                    Yz.wwwDir + "jquery/js/jquery.js",

                    // on success
                    function() {
                        console.log("Loaded jQuery: version " + window.jQuery.fn.jquery);
                        for(var i=0; i<self._callbacks.length; i++) {
                            self._callbacks[i]();
                        }
                        // reassign the function to remove checks
                        Yz.jquery = function(newcallback) {
                            typeof newcallback == "function" && newcallback();
                            return Yz;
                        }
                    },

                    // on error
                    function() {
                        console.log("Failed to load jQuery");
                        // reassign the function to remove checks
                        Yz.jquery = function(newcallback) {
                            throw "jQuery Not Loaded";
                        }
                    }
                );
            }
            if (typeof callback == "function")
                self._callbacks.push(callback);

        }
        return Yz;
    }

    /**
     *  `require' jQueryUI
     *  on successful load (or if already loaded), execute callback
     *  @param callback: void function(void)
     */
    Yz.jQueryUI = Yz.jqueryui = function(callback)
    {
        var ui = window.jQuery && window.jQuery.ui && window.jQuery.ui.version;
        if (ui) {
            console.log("jQuery UI version: " + ui );
            callback && callback();
        } else {
            var self = Yz.jqueryui;
            // create a stack of callbacks to execute on successful load of jQuery
            if (self._callbacks) {
                console.log("Yz.jQuery already called, but jquery.js not loaded yet");
            } else {
                self._callbacks = [];
                Yz.jQuery( function() {
                    Yz.fetchStyleSheet(
                        Yz.wwwDir + "jquery/css/jquery-ui.css"
                    );
                    Yz.fetchScript(
                        Yz.wwwDir + "jquery/js/jquery-ui.js",

                        // on success
                        function() {
                            console.log("Loaded jQuery UI: version " + window.jQuery.ui.version);
                            for(var i=0; i<self._callbacks.length; i++) {
                                self._callbacks[i]();
                            }
                            // reassign the function to remove checks
                            Yz.jqueryui = function(newcallback) {
                                typeof newcallback == "function" && newcallback();
                                return Yz;
                            }
                        },

                        // on error
                        function() {
                            console.log("Failed to load jQuery UI");
                            // reassign the function to remove checks
                            Yz.jqueryui = function(newcallback) {
                                throw "jQuery UI Not Loaded";
                            }
                        }
                    );
                });
            }
            if (typeof callback == "function")
                self._callbacks.push(callback);
        }
        return Yz;
    }


    /** load messages from script ???
     *
     *
     */
    Yz.translate =  function(message)
    {
        return message;
    }

    /**
     *  alert user with a message using a modal dialog
     *
     */
    Yz.Alert = function(message, title)
    {
        Yz.jqueryui(function(){
            var dlg = Yz.Alert.dialog;
            if (! dlg) {
                dlg = document.createElement("DIV");
                dlg.className = "yz-dialog";
                var buttons = {};
                buttons["Cancel"] = function() { $(this).dialog("close"); }
                Yz.Alert.dialog = dlg;
                Yz.Alert.buttons = buttons;
            }
            if (! title)
                title = Yz.translate("Alert");
            $(dlg)
                .text(message)
                .dialog({
                    modal: true,
                    title: title,
                    buttons: Yz.Alert.buttons
                });
            return Yz;
        });
        return Yz;
    }
    /**
     *  notify user with a message using a non-blocking UI element
     *
     */
    Yz.Notify = function(message)
    {
        alert(message);
        return Yz;
    }

    /** submit form using AJAX
     *  @param HTMLFormElement form
     *  @param callback onsuccess: void onsuccess(data)
     *  ?? @param callback onerror: void onerror()
     *  @return Yz
     */
    Yz.submitForm = function(theForm, onsuccess, onerror)
    {
        var data = {};
        for (i=0; i<theForm.elements.length; i++) {
            var el = theForm.elements[i];
            switch (el.type) {
                case "checkbox":
                case "hidden":
                case "radio":
                case "text":
                case "textarea":
                    data[el.name] = el.value;
                    break;
                case "select":
                    data[el.name] = el.options[el.selectedIndex].value;
                    break;
                case "submit":
                default:
            }
        }
        console.log(data);
        $.post(theForm.action, data, onsuccess);
        return Yz;
    }


    /********************************
     *      Private functions       *
     ********************************/

    Yz.fetchScript = function(script, onsuccess, onerror)
    {
        console.log('Fetch script: ' + script);
        Yz.block();
        var plugin = document.createElement("SCRIPT");
        plugin.src = script;
        plugin.type = 'text/javascript';
        if (plugin.readyState) {  // IE legacy
            plugin.onreadystatechange = function(){
                if (plugin.readyState == "loaded"
                        || plugin.readyState == "complete") {
                    plugin.onreadystatechange = null;
                    plugin.onload();
                }
            };
        }
        plugin.onload = function()
        {
            onsuccess && onsuccess();
            Yz.unblock();
        }
        plugin.onerror = function()
        {
            onerror && onerror();
            Yz.unblock();
        }
        document.getElementsByTagName("HEAD")[0].appendChild(plugin);
        return Yz;
    }

    Yz.fetchStyleSheet = function(href, onsuccess, onerror)
    {
        Yz.block();
        var plugin = document.createElement("LINK");
        plugin.href = href;
        plugin.setAttribute('rel', 'stylesheet');
        plugin.setAttribute('type', 'text/css');
        plugin.onload = function()
        {
            onsuccess && onsuccess();
            Yz.unblock();
        }
        plugin.onerror = function()
        {
            onerror && onerror();
            Yz.unblock();
        }
        document.getElementsByTagName("HEAD")[0].appendChild(plugin);
        return Yz;
    }


})();
